extern "C"
{
#include "../renderer/tr_local.h"
}
#include "tr_r2.h"

using namespace TDP;

cvar_t	*r_mode;
cvar_t	*r_fullscreen;
cvar_t	*r_customwidth;
cvar_t	*r_customheight;
cvar_t	*r_customaspect;
cvar_t	*r_subdivisions;
cvar_t	*r_glDriver;
cvar_t	*r_ignorehwgamma;
cvar_t	*r_gamma;
cvar_t	*r_colorbits;
cvar_t	*r_stencilbits;
cvar_t	*r_depthbits;
cvar_t	*r_stereo;
cvar_t	*r_verbose;

cvar_t	*r_logFile;
cvar_t	*r_drawBuffer;
cvar_t	*r_swapInterval;
cvar_t	*r_allowExtensions;
cvar_t	*r_ext_compressed_textures;
cvar_t	*r_ext_gamma_control;
cvar_t	*r_ext_multitexture;
cvar_t	*r_ext_compiled_vertex_array;
cvar_t	*r_ext_texture_env_add;
cvar_t	*r_displayRefresh;


void		R2_AddPolyToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts, int num )
{
}

void		R2_AddAdditiveLightToScene( const vec3_t org, float intensity, float r, float g, float b )
{
}

void		R2_UploadCinematic (int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty)
{
}



static void AssertCvarRange( cvar_t *cv, float minVal, float maxVal, qboolean shouldBeIntegral )
{
	if ( shouldBeIntegral )
	{
		if ( ( int ) cv->value != cv->integer )
		{
			ri.Printf( PRINT_WARNING, "WARNING: cvar '%s' must be integral (%f)\n", cv->name, cv->value );
			ri.Cvar_Set( cv->name, va( "%d", cv->integer ) );
		}
	}

	if ( cv->value < minVal )
	{
		ri.Printf( PRINT_WARNING, "WARNING: cvar '%s' out of range (%f < %f)\n", cv->name, cv->value, minVal );
		ri.Cvar_Set( cv->name, va( "%f", minVal ) );
	}
	else if ( cv->value > maxVal )
	{
		ri.Printf( PRINT_WARNING, "WARNING: cvar '%s' out of range (%f > %f)\n", cv->name, cv->value, maxVal );
		ri.Cvar_Set( cv->name, va( "%f", maxVal ) );
	}
}



/*
===============
R_SetColorMappings
===============
*/
void R_SetColorMappings( void ) {
	int		i, j;
	float	g;
	int		inf;
	int		shift;
	unsigned short s_gammatable[256];

	if ( r_gamma->value < 0.5f ) {
		ri.Cvar_Set( "r_gamma", "0.5" );
	} else if ( r_gamma->value > 3.0f ) {
		ri.Cvar_Set( "r_gamma", "3.0" );
	}

	g = r_gamma->value / 2.2f;

	// Riot - Shaders use a piecewise linear gamma approximation
	// This corrects that into linear space, then into user-specified gamma, then into sRGB
	float interpolationPoints[10] = { 0.0f, 0.0f, 0.014349874706803f, 0.125f, 0.050876088171557f, 0.25f, 0.21404114048223f, 0.5f, 1.0f, 1.0f };

	for(i=0;i<256;i++)
	{
		float baseValue = ((float)i) / 255.0f;

		// Figure out what this output value would be in linear space
		int j;
		for(j=2;;j+=2)
			if(interpolationPoints[j+1] >= baseValue)
				break;

		float t = (baseValue - interpolationPoints[j-1]) / (interpolationPoints[j+1] - interpolationPoints[j-1]);
		float linearValue = interpolationPoints[j-2] + t*(interpolationPoints[j]-interpolationPoints[j-2]);
		linearValue = pow(linearValue, g);

		// Calculate the sRGB value for this
		float srgbValue;
		if(linearValue < 0.0031308)
			srgbValue = linearValue * 12.92;
		else
			srgbValue = 1.055 * pow((double)linearValue, 1.0 / 2.4) - 0.055;
		if(srgbValue > 1.0f) srgbValue = 1.0f;
		if(srgbValue < 0.0f) srgbValue = 0.0f;
		
		s_gammatable[i] = (unsigned short)(65536.0f * srgbValue);
	}

	if ( glConfig.deviceSupportsGamma )
	{
		GLimp_SetGamma( s_gammatable, s_gammatable, s_gammatable );
	}
}


void		R2_BeginRegistration( glconfig_t *glconfigOut )
{
	r_mode = ri.Cvar_Get( "r_mode", "3", CVAR_ARCHIVE | CVAR_LATCH );
	r_fullscreen = ri.Cvar_Get( "r_fullscreen", "1", CVAR_ARCHIVE | CVAR_LATCH );
	r_customwidth = ri.Cvar_Get( "r_customwidth", "1680", CVAR_ARCHIVE | CVAR_LATCH );
	r_customheight = ri.Cvar_Get( "r_customheight", "1050", CVAR_ARCHIVE | CVAR_LATCH );
	r_customaspect = ri.Cvar_Get( "r_customaspect", "1", CVAR_ARCHIVE | CVAR_LATCH );
	r_subdivisions = ri.Cvar_Get ("r_subdivisions", "4", CVAR_ARCHIVE | CVAR_LATCH);
	r_glDriver = ri.Cvar_Get( "r_glDriver", OPENGL_DRIVER_NAME, CVAR_ARCHIVE | CVAR_LATCH );
	r_ignorehwgamma = ri.Cvar_Get( "r_ignorehwgamma", "0", CVAR_ARCHIVE | CVAR_LATCH);
	r_gamma = ri.Cvar_Get( "r_gamma", "1", CVAR_ARCHIVE );
	r_colorbits = ri.Cvar_Get( "r_colorbits", "0", CVAR_ARCHIVE | CVAR_LATCH );
#ifdef __linux__
	r_stencilbits = ri.Cvar_Get( "r_stencilbits", "0", CVAR_ARCHIVE | CVAR_LATCH );
#else
	r_stencilbits = ri.Cvar_Get( "r_stencilbits", "8", CVAR_ARCHIVE | CVAR_LATCH );
#endif
	r_depthbits = ri.Cvar_Get( "r_depthbits", "0", CVAR_ARCHIVE | CVAR_LATCH );

	r_logFile = ri.Cvar_Get( "r_logFile", "0", CVAR_CHEAT );
	r_drawBuffer = ri.Cvar_Get( "r_drawBuffer", "GL_BACK", CVAR_CHEAT );
	r_swapInterval = ri.Cvar_Get( "r_swapInterval", "0", CVAR_ARCHIVE );
	r_allowExtensions = ri.Cvar_Get( "r_allowExtensions", "1", CVAR_ARCHIVE | CVAR_LATCH );
	r_ext_compressed_textures = ri.Cvar_Get( "r_ext_compressed_textures", "0", CVAR_ARCHIVE | CVAR_LATCH );
	r_ext_gamma_control = ri.Cvar_Get( "r_ext_gamma_control", "1", CVAR_ARCHIVE | CVAR_LATCH );
	r_ext_multitexture = ri.Cvar_Get( "r_ext_multitexture", "1", CVAR_ARCHIVE | CVAR_LATCH );
	r_ext_compiled_vertex_array = ri.Cvar_Get( "r_ext_compiled_vertex_array", "1", CVAR_ARCHIVE | CVAR_LATCH);
#ifdef __linux__ // broken on linux
	r_ext_texture_env_add = ri.Cvar_Get( "r_ext_texture_env_add", "0", CVAR_ARCHIVE | CVAR_LATCH);
#else
	r_ext_texture_env_add = ri.Cvar_Get( "r_ext_texture_env_add", "1", CVAR_ARCHIVE | CVAR_LATCH);
#endif

	r_stereo = ri.Cvar_Get( "r_stereo", "0", CVAR_ARCHIVE | CVAR_LATCH );
	r_verbose = ri.Cvar_Get( "r_verbose", "0", CVAR_CHEAT );

	r_displayRefresh = ri.Cvar_Get( "r_displayRefresh", "0", CVAR_LATCH );
	AssertCvarRange( r_displayRefresh, 0, 200, qtrue );

	char renderer_buffer[1024];

	//
	// initialize OS specific portions of the renderer
	//
	// GLimp_Init directly or indirectly references the following cvars:
	//		- r_fullscreen
	//		- r_glDriver
	//		- r_mode
	//		- r_(color|depth|stencil)bits
	//		- r_ignorehwgamma
	//		- r_gamma
	//
	
	if ( glConfig.vidWidth == 0 )
	{
		GLint		temp;
		
		GLimp_Init();

		strcpy( renderer_buffer, glConfig.renderer_string );
		Q_strlwr( renderer_buffer );

		// OpenGL driver constants
		qglGetIntegerv( GL_MAX_TEXTURE_SIZE, &temp );
		glConfig.maxTextureSize = temp;

		// stubbed or broken drivers may have reported 0...
		if ( glConfig.maxTextureSize <= 0 ) 
		{
			glConfig.maxTextureSize = 0;
		}
	}

	R_SetColorMappings();
	R2_InitTBR();

	*glconfigOut = glConfig;
}

void		R2_SetWorldVisData( const byte *vis )
{
}

qhandle_t	R2_RegisterSkin( const char *name )
{
	return 1;
}


qhandle_t	R2_RegisterShaderNoMip( const PanicHandler *ph, const char *name )
{
	return R2_RegisterShader(ph, name);
}

qhandle_t	R2_RegisterShaderFromImage(const char *name, int lightmapIndex, image_t *image, qboolean mipRawImage)
{
	return 0;
}

void		R2_WaitForLoader( void )
{
}

void		R2_EndRegistration( void )
{
	R2_WaitForLoader();
}

int			R2_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection,
				   int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer )
{
	return 0;
}

int			R2_LerpTag( orientation_t *tag, qhandle_t handle, int startFrame, int endFrame, 
					 float frac, const char *tagName )
{
	AxisClear( tag->axis );
	VectorClear( tag->origin );
	return qfalse;
}

void		R2_ModelBounds( qhandle_t handle, vec3_t mins, vec3_t maxs )
{
	VectorClear(mins);
	VectorClear(maxs);
}

int			R2_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir )
{
	return qfalse;
}

void		R2_RegisterFont(const char *fontName, int pointSize, fontInfo_t *font)
{
}

void		R2_RemapShader(const char *oldShader, const char *newShader, const char *timeOffset)
{
}

// !!! Unused !!!
qboolean	R2_GetEntityToken( char *buffer, int size )
{
	return qfalse;
}

qboolean	R2_inPVS( const vec3_t p1, const vec3_t p2 )
{
	return qtrue;
}



/*
** R_GetModeInfo
*/
typedef struct vidmode_s
{
    const char *description;
    int         width, height;
	float		pixelAspect;		// pixel width / height
} vidmode_t;

vidmode_t r_vidModes[] =
{
    { "Mode  0: 320x240",		320,	240,	1 },
    { "Mode  1: 400x300",		400,	300,	1 },
    { "Mode  2: 512x384",		512,	384,	1 },
    { "Mode  3: 640x480",		640,	480,	1 },
    { "Mode  4: 800x600",		800,	600,	1 },
    { "Mode  5: 960x720",		960,	720,	1 },
    { "Mode  6: 1024x768",		1024,	768,	1 },
    { "Mode  7: 1152x864",		1152,	864,	1 },
    { "Mode  8: 1280x1024",		1280,	1024,	1 },
    { "Mode  9: 1600x1200",		1600,	1200,	1 },
    { "Mode 10: 2048x1536",		2048,	1536,	1 },
    { "Mode 11: 856x480 (wide)",856,	480,	1 },
    { "Mode 12: 1280x800 (wide)",1280,	800,	1 },
};
static int	s_numVidModes = ( sizeof( r_vidModes ) / sizeof( r_vidModes[0] ) );

extern "C" qboolean R_GetModeInfo( int *width, int *height, float *windowAspect, int mode )
{
	vidmode_t	*vm;

    if ( mode < -1 ) {
        return qfalse;
	}
	if ( mode >= s_numVidModes ) {
		return qfalse;
	}

	if ( mode == -1 ) {
		*width = r_customwidth->integer;
		*height = r_customheight->integer;
		*windowAspect = r_customaspect->value;
		return qtrue;
	}

	vm = &r_vidModes[mode];

    *width  = vm->width;
    *height = vm->height;
    *windowAspect = (float)vm->width / ( vm->height * vm->pixelAspect );

    return qtrue;
}

void		R2_Shutdown( qboolean destroyWindow )
{
	R2_ShutdownTBR();
	R2_ShutdownShaders();
	R2_ShutdownModels();

	if ( destroyWindow ) {
		GLimp_Shutdown();
	}
}
